﻿using System;
using System.Reflection;
using EPiServer.Core;
using EPiServer.PlugIn;
using TemplateFoundation.Exceptions;
using TemplateFoundation.WebControls.PropertyControls;

namespace TemplateFoundation.CustomProperties
{
    /// <summary>
    /// Base class for custom properties that use user controls for property rendering
    /// </summary>
    /// <typeparam name="T">The properties data type. This must be serializable to XML.</typeparam>
    /// <seealso cref="UserControlPropertyAttribute"/>
    public abstract class UserControlPropertyBase<T> : PropertyLongString where T : class
    {
        private string _editUserControlPath;
        private string _defaultUserControlPath;
        private string _onPageEditUserControlPath;

        /// <summary>
        /// Initializes a new instance of the <see cref="UserControlPropertyBase&lt;T&gt;"/> class.
        /// </summary>
        public UserControlPropertyBase()
        {
            // Verify class has been decorated with a PageDefinitionTypePlugIn attribute
            if(Attribute.GetCustomAttributes(GetType(), typeof(PageDefinitionTypePlugInAttribute)).Length==0)
            {
                throw new CustomAttributeFormatException("User control property type has not been decorated with a PageDefinitionTypePlugIn attribute");                
            }

            // Get the virtual path to the property user control through the UserControlProperty attribute
            var userControlPropertyAttributes = Attribute.GetCustomAttributes(GetType(), typeof(UserControlPropertyAttribute));

            if(userControlPropertyAttributes.Length==0)
            {
                throw new CustomAttributeFormatException("Class has not been decorated with a UserControlProperty attribute");
            }

            var attribute = (UserControlPropertyAttribute)userControlPropertyAttributes[0];

            _editUserControlPath = attribute.EditUserControlPath;
            _defaultUserControlPath = attribute.DefaultUserControlPath;
            _onPageEditUserControlPath = attribute.OnPageEditUserControlPath;
        }

        /// <summary>
        /// Creates the property control.
        /// </summary>
        /// <returns></returns>
        public override IPropertyControl CreatePropertyControl()
        {
            return new PropertyUserControlDispatcher<T>(_editUserControlPath,_defaultUserControlPath,_onPageEditUserControlPath);
        }

        /// <summary>
        /// Gets or sets the property value using typed objects
        /// </summary>
        public T TypedValue
        {
            get
            {
                var str = base.Value as string;

                if(string.IsNullOrEmpty(str))
                {
                    return null;
                }

                try
                {
                    return Serialization.XmlSerializer.Deserialize<T>(str);
                }
                catch (Exception)
                {
                    return null;
                }
            }
            set
            {
                if(value==null)
                {
                    base.Value = null;
                }
                else
                {
                    try
                    {
                        base.Value = Serialization.XmlSerializer.Serialize(value);
                    }
                    catch (InvalidCastException)
                    {
                        throw new TemplateFoundationException(string.Format("Unable to save property value for '{0}'. Value was of type '{1}' but an object of type '{2}' was expected.",
                            Name,
                            value.GetType().Name,
                            typeof(T).Name));
                    }
                }
            }
        }
    }

    /// <summary>
    /// Base class for custom properties that use user controls for property rendering
    /// </summary>
    /// <seealso cref="UserControlPropertyAttribute"/>
    public abstract class UserControlPropertyBase : UserControlPropertyBase<string>
    {

    }
}
